package com.test.demo.Util;


import com.test.demo.constant.RedisStorageLayerEnum;
import io.lettuce.core.RedisCommandExecutionException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author zx
 * @date 2021/08/30
 */
@Component
@Slf4j
public class RedisUtil {

    @Resource
    private RedisTemplate redisTemplate;

    public RedisTemplate getTemplate() {
        return this.redisTemplate;
    }


    /**
     * 指定缓存失效时间
     *
     * @param key  键
     * @param time 时间(秒)
     * @return
     */
    public boolean expire(RedisStorageLayerEnum key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key.getLayered(), time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 指定缓存失效时间
     *
     * @param key  键
     * @param time 时间(秒)
     * @param unit 时间单位
     * @return
     */
    public boolean expire(RedisStorageLayerEnum key, long time, TimeUnit unit) {
        try {
            if (time > 0) {
                redisTemplate.expire(key.getLayered(), time, unit);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据key 获取过期时间
     *
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(RedisStorageLayerEnum key) {
        return redisTemplate.getExpire(key.getLayered(), TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(RedisStorageLayerEnum key) {
        try {
            return redisTemplate.hasKey(key.getLayered());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     *
     * @param keys 可以传一个值 或多个
     */
    public void del(RedisStorageLayerEnum... keys) {
        if (keys != null && keys.length > 0) {
            if (keys.length == 1) {
                redisTemplate.delete(keys[0].getLayered());
            } else {
                List<String> list = new ArrayList<>();
                for (RedisStorageLayerEnum key : keys) {
                    list.add(key.getLayered());
                }
                redisTemplate.delete(list);
            }
        }
    }

    //============================String=============================

    /**
     * 普通缓存获取
     *
     * @param key 键
     * @return 值
     */
    public Object get(RedisStorageLayerEnum key) {
        return key == null ? null : redisTemplate.opsForValue().get(key.getLayered());
    }

    /**
     * 普通缓存放入
     *
     * @param key   键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(RedisStorageLayerEnum key, Object value) {
        try {
            redisTemplate.opsForValue().set(key.getLayered(), value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }

    /**
     * 普通缓存放入并设置时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(RedisStorageLayerEnum key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key.getLayered(), value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    //================================Map=================================

    /**
     * 获取hash数据类型中单一项的值
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return 值
     */
    public Object hashGet(RedisStorageLayerEnum key, Object item) {
        return redisTemplate.opsForHash().get(key.getLayered(), item);
    }

    /**
     * 获取hashKey对应的所有键值
     *
     * @param key 键
     * @return 对应的多个键值
     */
    public Map<Object, Object> hashGet(RedisStorageLayerEnum key) {
        return redisTemplate.opsForHash().entries(key.getLayered());
    }

    /**
     * Hash类型进行存储
     *
     * @param key 键
     * @param map 对应多个键值
     * @return true 成功 false 失败
     */
    public boolean hashSet(RedisStorageLayerEnum key, Map<Object, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key.getLayered(), map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Hash类型进行存储并进行设施过期时间 并设置时间
     *
     * @param key  键
     * @param map  对应多个键值
     * @param time 时间(秒)
     * @return true成功 false失败
     */
    public boolean hashSet(RedisStorageLayerEnum key, Map<Object, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key.getLayered(), map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @return true 成功 false失败
     */
    public boolean hashSet(RedisStorageLayerEnum key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key.getLayered(), item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @param time  时间(秒)  注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    public boolean hashSet(RedisStorageLayerEnum key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key.getLayered(), item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除hash表中的值
     *
     * @param key  键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    public void hashDel(RedisStorageLayerEnum key, Object... item) {
        redisTemplate.opsForHash().delete(key.getLayered(), item);
    }

    /**
     * 判断hash表中是否有该项的值
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return true 存在 false不存在
     */
    public boolean hashHasKey(RedisStorageLayerEnum key, String item) {
        return redisTemplate.opsForHash().hasKey(key.getLayered(), item);
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     *
     * @param key  键
     * @param item 项
     * @param by   要增加几(大于0)
     * @return
     */
    public double hashIncr(RedisStorageLayerEnum key, String item, double by) {
        return redisTemplate.opsForHash().increment(key.getLayered(), item, by);
    }

    /**
     * hash递减
     *
     * @param key  键
     * @param item 项
     * @param by   要减少记(小于0)
     * @return
     */
    public double hashDecr(RedisStorageLayerEnum key, String item, double by) {
        return redisTemplate.opsForHash().increment(key.getLayered(), item, -by);
    }

    //============================set=============================

    /**
     * 根据key获取Set中的所有值
     *
     * @param key 键
     * @return
     */
    public Set<Object> setGetAll(RedisStorageLayerEnum key) {
        try {
            return redisTemplate.opsForSet().members(key.getLayered());
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据value从一个set中查询,是否存在
     *
     * @param key   键
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean setHasKey(RedisStorageLayerEnum key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key.getLayered(), value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将数据放入set缓存
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long setAdd(RedisStorageLayerEnum key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key.getLayered(), values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 将set数据放入缓存
     *
     * @param key    键
     * @param time   时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long setAdd(RedisStorageLayerEnum key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key.getLayered(), values);
            if (time > 0) {
                expire(key, time);
            }
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 获取set缓存的长度
     *
     * @param key 键
     * @return
     */
    public long setSize(RedisStorageLayerEnum key) {
        try {
            return redisTemplate.opsForSet().size(key.getLayered());
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 移除值为value的
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    public long setRemove(RedisStorageLayerEnum key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key.getLayered(), values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    //===============================list=================================

    /**
     * 获取list缓存的内容
     *
     * @param key   键
     * @param start 开始
     * @param end   结束  0 到 -1代表所有值
     * @return
     */
    public List<Object> listGet(RedisStorageLayerEnum key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key.getLayered(), start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取list集合中所有的元素
     *
     * @param key
     * @return
     */
    public List<Object> listGet(RedisStorageLayerEnum key) {
        return redisTemplate.opsForList().range(key.getLayered(), 0, -1);
    }

    /**
     * 获取list缓存的长度
     *
     * @param key 键
     * @return
     */
    public long listGetListSize(RedisStorageLayerEnum key) {
        try {
            return redisTemplate.opsForList().size(key.getLayered());
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 通过索引 获取list中的值
     *
     * @param key   键
     * @param index 索引  index>=0时， 0 表头，1 第二个元素，依次类推；index<0时，-1，表尾，-2倒数第二个元素，依次类推
     * @return
     */
    public Object listGetIndex(RedisStorageLayerEnum key, long index) {
        try {
            return redisTemplate.opsForList().index(key.getLayered(), index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean listSet(RedisStorageLayerEnum key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key.getLayered(), value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存，并设置过期时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    public boolean listSet(RedisStorageLayerEnum key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key.getLayered(), value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean listSet(RedisStorageLayerEnum key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key.getLayered(), value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存,并设置过期时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    public boolean listSet(RedisStorageLayerEnum key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key.getLayered(), value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据索引修改list中的某条数据
     *
     * @param key   键
     * @param index 索引
     * @param value 值
     * @return
     */
    public boolean listUpdateIndex(RedisStorageLayerEnum key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key.getLayered(), index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 移除count个值为value
     *
     * @param key   键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
    public long listRemove(RedisStorageLayerEnum key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key.getLayered(), count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    //=================================zset===================================================


    /**
     * zset添加元素
     *
     * @param key
     * @param score
     * @param values
     * @return
     */
    public boolean zsetAdd(RedisStorageLayerEnum key, double score, long time, Object... values) {
        Boolean add = redisTemplate.opsForZSet().add(key.getLayered(), values, score);
        if (time > 0) {
            expire(key, time);
        }
        return add;
    }

    /**
     * 获取指定下标的zset键值对
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set zsetRange(RedisStorageLayerEnum key, long start, long end) {
        Set range = redisTemplate.opsForZSet().range(key.getLayered(), start, end);
        return range;

    }

    /**
     * 获取所有的zset键值对
     *
     * @param key
     * @return
     */
    public Set zsetRangeAll(RedisStorageLayerEnum key) {
        Set range = redisTemplate.opsForZSet().range(key.getLayered(), 0, -1);
        return range;
    }

    /**
     * 获取带有sorce 的指定下标的set键值对
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<ZSetOperations.TypedTuple<Object>> rangeWithScores(RedisStorageLayerEnum key, long start, long end) {
        Set set = redisTemplate.opsForZSet().rangeWithScores(key.getLayered(), start, end);
        return set;
    }

    /**
     * 获取带有sorce的所有的键值对
     *
     * @param key
     * @return
     */
    public Set<ZSetOperations.TypedTuple<Object>> rangeWithScoresAll(RedisStorageLayerEnum key) {
        Set set = redisTemplate.opsForZSet().rangeWithScores(key.getLayered(), 0, -1);
        return set;
    }

    /**
     * 获取score分数区间内的集合
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set rangeByScore(RedisStorageLayerEnum key, double min, double max) {
        Set set = redisTemplate.opsForZSet().rangeByScore(key.getLayered(), min, max);
        return set;
    }

    /**
     * 返回带有score的 ，分数区间再[min,max]之间的集合
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    Set<ZSetOperations.TypedTuple<Object>> rangeByScoreWithScores(RedisStorageLayerEnum key, double min, double max) {
        Set set = redisTemplate.opsForZSet().rangeByScoreWithScores(key.getLayered(), min, max);
        return set;
    }

    /**
     * 返回带有score的，分数区间再[min,max]之间的，跳过offset条数据，总共取count条数据
     *
     * @param key
     * @param min
     * @param max
     * @param offset 跳过几条数据 （相当于sql中的offset）
     * @param count  取几条（相当于sql中的limit）
     * @return
     */
    Set<ZSetOperations.TypedTuple<Object>> rangeByScoreWithScores(RedisStorageLayerEnum key, double min, double max, long offset, long count) {
        Set set = redisTemplate.opsForZSet().rangeByScoreWithScores(key.getLayered(), min, max, offset, count);
        return set;
    }

    /**
     * 获取zset集合的长度
     *
     * @param key
     * @return
     */
    public long zsetSize(RedisStorageLayerEnum key) {
        return redisTemplate.opsForZSet().zCard(key.getLayered());
    }

    /**
     * 去重，而且会把重复的数据score进行相加，返回值是这个集合的长度
     * key，otherKey这两个key的有序队列进行相加，如果两个队列中存在相同的value，就将value的score相加，最终将最后的组合结果放到destKey中。
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long zsetUnionAndStore(RedisStorageLayerEnum key, RedisStorageLayerEnum otherKey, RedisStorageLayerEnum destKey) {
        return redisTemplate.opsForZSet().unionAndStore(key.getLayered(), otherKey, destKey);
    }

    /***
     * 这个other可以是集合，上面方法的重载
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long zsetUnionAndStore(RedisStorageLayerEnum key, Collection<Object> otherKeys, RedisStorageLayerEnum destKey) {
        return redisTemplate.opsForZSet().unionAndStore(key.getLayered(), otherKeys, destKey);
    }


    /**
     * 将key和otherkey中元素的集合取其交集，放进destKye中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long zsetIntersectAndStore(RedisStorageLayerEnum key, RedisStorageLayerEnum otherKey, RedisStorageLayerEnum destKey) {
        return redisTemplate.opsForZSet().intersectAndStore(key.getLayered(), otherKey, destKey);
    }

    /**
     * 上面方法的重载，otherkey可以是集合
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long zsetIntersectAndStore(RedisStorageLayerEnum key, Collection<Object> otherKeys, RedisStorageLayerEnum destKey) {
        return redisTemplate.opsForZSet().intersectAndStore(key.getLayered(), otherKeys, destKey);
    }

    /**
     * 根据key和value移除指定元素
     *
     * @param key
     * @param value
     * @return
     */
    public Long removeZset(RedisStorageLayerEnum key, Object value) {
        return redisTemplate.opsForZSet().remove(key.getLayered(), value);
    }

    /**
     * 获取对应key和value的score
     *
     * @param key
     * @param value
     * @return
     */
    public Double getScore(RedisStorageLayerEnum key, Object value) {
        return redisTemplate.opsForZSet().score(key.getLayered(), value);
    }

    /**
     * 指定元素增加(减少)指定值
     *
     * @param key
     * @param obj
     * @param score
     * @return
     */
    public Object zsetUpdateScore(RedisStorageLayerEnum key, Object obj, double score) {
        return redisTemplate.opsForZSet().incrementScore(key.getLayered(), obj, score);
    }


    //=============================计数===============================

    /**
     * 实现计数功能，并返回当前计数结果
     *
     * @param key
     * @param values
     * @return
     */
    public synchronized long incrent(RedisStorageLayerEnum key, long values) {
        try {
            //有键为key的则进行加 values，没有则进行创建再加values
            redisTemplate.opsForValue().increment(key.getLayered(), values);
        } catch (RedisCommandExecutionException e) {
            log.error("{},此键已经存在，不能进行计数功能。", key.getLayered());
            e.printStackTrace();
        }
        return Long.valueOf(String.valueOf(redisTemplate.opsForValue().get(key.getLayered())));
    }
}

